package org.p2psimulator.entity.behavior;

import java.util.Random;
import java.util.Vector;

import org.p2psimulator.entity.Id;
import org.p2psimulator.entity.Message;
import org.p2psimulator.entity.OverlayNetwork;
import org.p2psimulator.entity.reputation.Reputation;
import org.p2psimulator.entity.reputation.ReputationManager;
import org.p2psimulator.entity.reputation.ReputationRepository;
import org.p2psimulator.exceptions.ReputationRepositoryException;

/**
 * Um Behavior determina o comportamento de um peer no sistema,
 * quanto a resposta e encaminhamento de mensagens e avaliação
 * de peers, recursos e transações.
 * @author amorim
 *
 */
public abstract class Behavior {

    protected Double thisReputation = 0d;
    protected Double interacitonCount = 0d;

    /**
     * Indica se um peer 'rater' deve ou não responder a uma
     * mensagem 'msg'
     * @param rater
     * @param msg
     * @return
     */
    public abstract boolean answareMessage(Id rater, Message msg);

    public abstract boolean rate();

    public abstract boolean damageResource();

    public abstract boolean transacNeutro(BehaviorTypes rateeBehavior, Id ratee);

    /**
     * Indica como um peer 'rater' deve avaliar um outro peer 'ratee'
     * @param rater
     * @param ratee
     * @return 	-1<= ret <= 1
     */
    public abstract double ratePeer(Id rater, Id ratee, BehaviorTypes rateeBehavior, ReputationRepository repository);

    /**
     * Indica como um peer 'rater' deve avaliar uma transação
     * com outro peer 'ratee'
     * @param rater
     * @param ratee
     * @param transResult
     * @return 	1 = boa avalia��oo
     * 		   	0 = avalia��o neutra
     * 			-1 = avalia��o ruim
     */
    public abstract double rateTransaction(Id rater, Id ratee, int transResult, BehaviorTypes rateeBehavior, ReputationRepository repository);

    /**
     * Indica como um peer 'rater' deve avaliar um recurso 'resource'
     * @param rater
     * @param resource
     * @return 	1 = boa avalia��o
     * 		   	0 = avalia��o neutra
     * 			-1 = avalia��o ruim
     */
    public abstract double rateResource(Id rater, Id resource, ReputationRepository repository);

    /**
     * Retorna os peers reput�veis do reposit�rio local de rater
     * Dependendo do seu tipo de comportamento, os peers mais
     * reput�veis conhecidos por rater n�o s�o necessariamente os de maior
     * reputa��o (e.g. peers corruptos)
     * @param rater
     * @param resource
     * @return 	1 = boa avalia��o
     * 		   	0 = avalia��o neutra
     * 			-1 = avalia��o ruim
     */
    public Vector<Id> getBestKnownPeers(Id rater,
            ReputationRepository repository, OverlayNetwork overlay) {
        Vector<Id> vector = new Vector<Id>();
        try {
            for (Id ratee : repository.getReputationMap().get(rater).keySet()) {
                Reputation rep = repository.getLocalReputation(rater, ratee);
                if (rep.getNormalReputation() >= 0) {
                    vector.add(ratee);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        if (vector.size() == 0) {
            vector.add(overlay.getOnlineRandomPeer().getId());
            vector.add(overlay.getOnlineRandomPeer().getId());
            vector.add(overlay.getOnlineRandomPeer().getId());
        }
        return vector;
    }

    public abstract BehaviorTypes getBehaviorType();

    public boolean leaveNetwork() {
        Random r = new Random();
        int k = r.nextInt(101);
        return k > 60;
    }

    public void updateReputation(Id rater, ReputationRepository repository) {
        synchronized (repository) {
            try {

                ReputationManager manager = repository.getManager();
                //thisReputation = manager.getGlobalBehaviorReputation(getBehaviorType())/100;

                thisReputation = repository.getGlobalReputation(rater);

                if (thisReputation.isNaN()) {
                    thisReputation = 0d;
                }
            }catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public Double getInteracitonCount() {
        return interacitonCount;
    }

    public void setInteracitonCount(Double interacitonCount) {
        this.interacitonCount = interacitonCount;
    }

    
}
